package com.coolw.spring.statemachine.support.order;

import com.coolw.spring.statemachine.constant.StateMachineConst;
import com.coolw.spring.statemachine.enums.MachineIdEnum;
import com.coolw.spring.statemachine.enums.OrderEvent;
import com.coolw.spring.statemachine.enums.OrderStatusEnum;
import com.coolw.spring.statemachine.support.AbstractStateMachineBuilder;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.statemachine.StateMachine;
import org.springframework.statemachine.action.Action;
import org.springframework.statemachine.config.StateMachineBuilder;
import org.springframework.statemachine.guard.Guard;
import org.springframework.statemachine.state.State;

import java.util.EnumSet;
import java.util.Map;
import java.util.Objects;

/**
 * 订单状态机构建
 *
 * @author coolw
 * @date 2024/4/17 15:09
 */
@Slf4j
@Configuration
public class OrderStateMachineBuilder extends AbstractStateMachineBuilder<OrderStatusEnum, OrderEvent> {

    public final static String MACHINE_ID = "orderStateMachine";

    @Override
    public MachineIdEnum getMachineId() {
        return MachineIdEnum.ORDER;
    }

    @Override
    public StateMachine<OrderStatusEnum, OrderEvent> builder(BeanFactory beanFactory) throws Exception {
        StateMachineBuilder.Builder<OrderStatusEnum, OrderEvent> builder = StateMachineBuilder.builder();

        builder.configureConfiguration()
                .withConfiguration()
                .machineId(MACHINE_ID)
                .beanFactory(beanFactory);

        builder.configureStates()
                .withStates()
                .initial(OrderStatusEnum.WAIT_PAYMENT)
                .end(OrderStatusEnum.FINISH)
                .states(EnumSet.allOf(OrderStatusEnum.class));

        builder.configureTransitions()
                // 支付事件:待支付 -> 待发货
                .withExternal()
                .source(OrderStatusEnum.WAIT_PAYMENT).target(OrderStatusEnum.WAIT_DELIVER)
                .event(OrderEvent.PAYED)
                .guard(guard())
                //.action(action(), errorAction())
                .and()
                // 发货事件:待发货 -> 待收货
                .withExternal()
                .source(OrderStatusEnum.WAIT_DELIVER).target(OrderStatusEnum.WAIT_RECEIVE)
                .event(OrderEvent.DELIVERY)
                .guard(guard())
                //.action(action(), errorAction())
                .and()
                // 收货事件:待收货 -> 已完成
                .withExternal()
                .source(OrderStatusEnum.WAIT_RECEIVE).target(OrderStatusEnum.FINISH)
                .event(OrderEvent.RECEIVED)
                .guard(guard());
        //.action(action(), errorAction());

        return builder.build();
    }

    @Bean
    public Guard<OrderStatusEnum, OrderEvent> guard() {
        return context -> {
            OrderContext orderContext = (OrderContext) context.getMessageHeader(StateMachineConst.CONTEXT_KEY);
            if (Objects.isNull(orderContext)) {
                log.warn("orderContext为空,context:{}", context);
                return false;
            }
            return true;
        };
    }

    @Bean
    public Action<OrderStatusEnum, OrderEvent> action() {
        return context -> {
            OrderContext orderContext = (OrderContext) context.getMessageHeader(StateMachineConst.CONTEXT_KEY);
            if (Objects.isNull(orderContext)) {
                log.error("orderContext信息为空,context:{}", context);
                return;
            }

            String orderId = orderContext.getOrderId();
            State<OrderStatusEnum, OrderEvent> source = context.getSource();
            State<OrderStatusEnum, OrderEvent> target = context.getTarget();
            OrderEvent event = context.getEvent();
            log.info("订单正在操作,orderId:{},from:{},to:{},on:{}", orderId, source.getId(), target.getId()
                    , event);
        };
    }

    private Action<OrderStatusEnum, OrderEvent> errorAction() {
        return context -> {
            Exception ex = context.getException();
            Map<Object, Object> varMap = context.getExtendedState().getVariables();
            varMap.put(StateMachineConst.HAS_ERROR, true);
            varMap.put(StateMachineConst.ERROR, ex);
            varMap.put(StateMachineConst.MESSAGE, ex.getMessage());
        };
    }
}
